﻿using System;
using System.Web.Routing;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace MvcSupplantCaching.UnitTests
{
    [TestClass]
    public class OutputCacheManagerTest
    {
        private KeyBuilder _keyBuilder;
        private OutputCacheManager _manager;
        private const string _testCacheKey = "testCacheKey";
        private const string _controllerName = "test";
        private const string _actionName = "index";
        private static readonly RouteValueDictionary RouteValues = new RouteValueDictionary { { "testKey", "testValue" } };
        private const int LongExpirationSeconds = 300;

        [TestInitialize]
        public void Initialize()
        {
            _keyBuilder = new KeyBuilder();
            _manager = OutputCacheManager.GetInstance(OutputCache.Instance, _keyBuilder);
        }

        [TestCleanup]
        public void Cleanup()
        {
            _manager = null;
        }

        private string BuildCacheKey(string controllerName, string actionName, RouteValueDictionary routeValues = null)
        {
            return _keyBuilder.BuildKey(controllerName, actionName, routeValues);
        }

        private void AddItemAndVerify(CacheItem cacheItem, string cacheKey, int? expirationSeconds = null)
        {
            // Add The Item
            _manager.AddItem(cacheKey, cacheItem, DateTime.UtcNow.AddSeconds(expirationSeconds ?? LongExpirationSeconds));

            // Verify it was added.
            var cachedItem = _manager.GetItem(cacheKey);
            Assert.AreEqual(cacheItem, cachedItem);
        }

        private void RemoveItemAndVerify(string controllerName, string actionName, RouteValueDictionary routeValues = null)
        {
            // Build the cache key.
            string cacheKey = BuildCacheKey(controllerName, actionName, routeValues);

            // Remove it
            _manager.RemoveItem(controllerName, actionName, routeValues);

            // Verify it was removed
            var cachedItem = _manager.GetItem(cacheKey);
            Assert.IsNull(cachedItem);
        }

        [TestMethod]
        public void AddItemTest()
        {
            var cacheItem = new CacheItem()
            {
                Content = "test content",
                ContentType = "text/html"
            };

            AddItemAndVerify(cacheItem, _testCacheKey);
        }

        [TestMethod]
        public void RemoveItemTest()
        {
            var cacheItem = new CacheItem()
            {
                Content = "test content",
                ContentType = "text/html"
            };

            string cacheKey = BuildCacheKey(_controllerName, _actionName, RouteValues);
            AddItemAndVerify(cacheItem, cacheKey);
            
            RemoveItemAndVerify(_controllerName, _actionName, RouteValues);
        }

        [TestMethod]
        public void RemoveItemsTest()
        {
            var cacheItem = new CacheItem()
            {
                Content = "test content",
                ContentType = "text/html"
            };

            // Group one: Removing based on controller and action should drop both of these.
            string cacheKeyOneA = BuildCacheKey(_controllerName, _actionName);
            string cacheKeyOneB = BuildCacheKey(_controllerName, _actionName, RouteValues);

            AddItemAndVerify(cacheItem, cacheKeyOneA);
            AddItemAndVerify(cacheItem, cacheKeyOneB);
            _manager.RemoveItems(_controllerName, _actionName);
            var cachedItem = _manager.GetItem(cacheKeyOneA);
            Assert.IsNull(cachedItem);
            cachedItem = _manager.GetItem(cacheKeyOneB);
            Assert.IsNull(cachedItem);

            // Group two: Removing based on controller, action, and routeValues should only drop one, since the route values differ.
            var routeValues = new RouteValueDictionary { {"item-b", "item B"} };
            string cacheKeyTwoA = BuildCacheKey(_controllerName, _actionName, routeValues);
            string cacheKeyTwoB = BuildCacheKey(_controllerName, _actionName, RouteValues);

            AddItemAndVerify(cacheItem, cacheKeyTwoA);
            AddItemAndVerify(cacheItem, cacheKeyTwoB);
            _manager.RemoveItems(_controllerName, _actionName, routeValues);
            cachedItem = _manager.GetItem(cacheKeyTwoA);
            Assert.IsNull(cachedItem);
            cachedItem = _manager.GetItem(cacheKeyTwoB);
            Assert.IsNotNull(cachedItem);

            // Group three: Controller, action, and some route values are shared, both should drop.
            string cacheKeyThreeA = BuildCacheKey(_controllerName, _actionName, RouteValues);
            routeValues = new RouteValueDictionary(RouteValues);
            routeValues.Add("another-key", "another value");
            string cacheKeyThreeB = BuildCacheKey(_controllerName, _actionName, routeValues);

            AddItemAndVerify(cacheItem, cacheKeyThreeA);
            AddItemAndVerify(cacheItem, cacheKeyThreeB);
            _manager.RemoveItems(_controllerName, _actionName, RouteValues);
            cachedItem = _manager.GetItem(cacheKeyThreeA);
            Assert.IsNull(cachedItem);
            cachedItem = _manager.GetItem(cacheKeyThreeB);
            Assert.IsNull(cachedItem);
        }
    }
}
